package cn.iocoder.boot.yudao.module.spider.shopee.seller;

import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.request.SellerRequest;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.SellerResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.account.SellerSessionResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.order.SellerMultiShopOrderResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.order.SellerOrderResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.orderid.SellerOrderIdListResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.orderid.SellerOrderIdResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.product.SellerShopProductDetailResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.product.SellerShopProductResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.shop.SellerShopListResponse;
import cn.iocoder.boot.yudao.module.spider.shopee.seller.response.shop.SellerShopResponse;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;

import java.util.*;
import java.util.function.Function;

/**
 * Shopee 卖家爬虫
 */
public class SellerSpider {
    public static HashMap<String, String> sellerHeaderMap = new HashMap<>();
    static {
        sellerHeaderMap.put("authority", "seller.shopee.cn");
        sellerHeaderMap.put("accept", "application/json, text/plain, */*");
        sellerHeaderMap.put("accept-language", "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6");
        sellerHeaderMap.put("sc-fe-ver", "22.12385");
        sellerHeaderMap.put("sec-ch-ua", "\"Not.A/Brand\";v=\"8\", \"Chromium\";v=\"114\", \"Microsoft Edge\";v=\"114\"");
        sellerHeaderMap.put("sec-ch-ua-mobile", "?0");
        sellerHeaderMap.put("sec-ch-ua-platform", "\"macOS\"");
        sellerHeaderMap.put("sec-fetch-dest", "empty");
        sellerHeaderMap.put("sec-fetch-mode", "cors");
        sellerHeaderMap.put("sec-fetch-site", "same-origin");
        sellerHeaderMap.put("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Safari/537.36 Edg/114.0.1823.51");
    }

    /**
     * 获取账户基本信息
     * @param request
     * @return
     */
    public static SellerSessionResponse getSession(SellerRequest request) {
       return doGet("https://seller.shopee.cn/api/cnsc/selleraccount/get_session/?SPC_CDS_VER=2", request.getCookie(), SellerSessionResponse.class);
    }

    /**
     * 获取账号下的全部
     * @return 店铺列表
     */
    public static List<SellerShopResponse> getMerchantShopFullList(SellerRequest request) {
        return getFullListByPage(p -> getMerchantShopList(request, p));
    }

    /**
     * 获取账号下的全部店铺
     *
     * @param pageNo    分页
     * @return 店铺列表
     */
    public static Page<SellerShopResponse> getMerchantShopList(SellerRequest request, Long pageNo) {
        String url = "https://seller.shopee.cn/api/cnsc/selleraccount/get_merchant_shop_list/?SPC_CDS_VER=2&cnsc_shop_id={}&page_index={}&page_size=50";
        SellerShopListResponse bean = doGet(StrUtil.format(url, request.getAccountId(), pageNo), request.getCookie(), SellerShopListResponse.class);

        Page<SellerShopResponse> page = new Page<>(pageNo, 50, bean.getData().getTotal());
        page.setRecords(bean.getData().getShops());
        return page;
    }

    /**
     * 获取店铺完整的订单列表信息
     * 订单列表主要分为两个请求，分别是获取订单ID列表和获取订单明细
     * 当前为获取全部订单ID列表
     * @param request 爬虫请求对象
     * @return
     */
    public static List<SellerOrderIdResponse> getOrderIdFullList(SellerRequest request) {
        return getFullListByPage(p -> getOrderIdList(request, p));
    }

    /**
     * 获取店铺订单ID列表
     * @param request 爬虫请求对象
     * @param pageNo 分页信息
     * @return
     */
    public static Page<SellerOrderIdResponse> getOrderIdList(SellerRequest request, Long pageNo) {
        String url = "https://seller.shopee.cn/api/v3/order/get_order_id_list?SPC_CDS={}&SPC_CDS_VER=2&page_size=40&page_number={}&sort_by=create_date_desc&backend_offset=&cnsc_shop_id={}&cbsc_shop_region={}";
        SellerOrderIdListResponse bean = doGet(StrUtil.format(url,request.getSpcCds(), pageNo, request.getShopId(), request.getRegionId()), request.getCookie(), SellerOrderIdListResponse.class);
        Page<SellerOrderIdResponse> page = new Page<>(pageNo, 40, bean.getData().getPageInfo().getTotal());
        page.setRecords(bean.getData().getOrders());
        return page;
    }

    /**
     * 按照商品ID列表获取订单明细，一般请求列表为5条
     * 订单列表主要分为两个请求，分别是获取订单ID列表和获取订单明细
     * 当前为获取全部订单ID列表
     * @param request
     * @param orderIdList
     * @return
     */
    public static List<SellerOrderResponse> getOrderListByOrderIdsMultiShop(SellerRequest request, List<SellerOrderIdResponse> orderIdList) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("from_seller_data", false);
        dataMap.put("orders", JSONUtil.toJsonStr(orderIdList));

        String url = "https://seller.shopee.cn/api/v3/order/get_order_list_by_order_ids_multi_shop?SPC_CDS={}&SPC_CDS_VER=2&cnsc_shop_id={}&cbsc_shop_region={}";
        SellerMultiShopOrderResponse bean = doPost(StrUtil.format(url, request.getSpcCds(), request.getShopId(), request.getRegionId()), request.getCookie(), dataMap, SellerMultiShopOrderResponse.class);
        return bean.getOrders();
    }


    /**
     * 获取店铺商品分页信息
     *
     * @return 商品列表
     */
    public static List<SellerShopProductDetailResponse> getMpskuList(SellerRequest request) {
        return getFullListByPage(p -> getMpskuList(request, p));
    }

    /**
     * 获取店铺商品分页信息
     *
     * @param pageNo 分页信息
     * @return 商品列表
     */
    public static Page<SellerShopProductDetailResponse> getMpskuList(SellerRequest request, Long pageNo) {
        String url = "https://seller.shopee.cn/api/v3/mpsku/get_mpsku_list_v2/?SPC_CDS_VER=2&page_number={}&page_size=12&list_type=all&source=seller_center&version=1.0.0&cnsc_shop_id={}&cbsc_shop_region=my";
        SellerShopProductResponse bean = doGet(StrUtil.format(url, request.getShopId(), pageNo), request.getCookie(), SellerShopProductResponse.class);
        Page<SellerShopProductDetailResponse> page = new Page<>(pageNo, 50, bean.getData().getPageInfo().getTotal());
        page.setRecords(bean.getData().getList());
        return page;
    }

    /**
     * 置顶商品
     * @param request 爬虫请求对象
     * @param productId 商品ID
     * @return 是否请求成功
     */
    public static boolean boostProduct(SellerRequest request, Long productId) {
        Map<String, Object> dataMap = new HashMap<>();
        dataMap.put("id", productId);
        String url = "https://seller.shopee.cn/api/v3/product/boost_product/?version=3.1.0&SPC_CDS={}&SPC_CDS_VER=2&cnsc_shop_id={}&cbsc_shop_region={}";
        SellerResponse bean = doPost(StrUtil.format(url, request.getSpcCds(), request.getShopId(), request.getRegionId()), request.getCookie(), dataMap, SellerResponse.class);
        return bean.getCode().equals(0);
    }


    ///////////// 基础Web请求 ///////////////

    /**
     * 计算总页码
     * @param total 数据总条数
     * @param pageSize 每页显示数量
     * @return 总页码
     */
    private static Long getPageTotal(Long total, Long pageSize) {
        return (total + pageSize - 1) / pageSize;
    }


    /**
     * GET请求URL地址，使用默认的Header信息，并设置请求cookie
     * @param url 请求地址
     * @param cookie 账户Cookie
     * @return Response Body
     */
    private static String doGet(String url, String cookie) {
        Map<String, String> header = new HashMap<>(sellerHeaderMap);
        header.put("cookie", cookie);
        return doRequest(url, "get", header, Collections.emptyMap());
    }

    /**
     * GET请求URL地址，使用默认Header信息，设置请求头Cookie信息，并将结果转换为T类型
     * @param url 请求地址
     * @param cookie cookie信息
     * @param clazz 返回结果类型
     * @return 返回结果
     * @param <T> 返回结果泛型
     */
    private static <T> T doGet(String url, String cookie, Class<T> clazz) {
        return JSONUtil.toBean(doGet(url, cookie), clazz);
    }

    /**
     * POST请求到URL地址，使用默认的Header信息，并设置请求Cookie，POST参数为data
     * @param url 请求地址
     * @param cookie cookie信息
     * @param data 数据列表
     * @return
     */
    private static String doPost(String url, String cookie, Map<String, Object> data) {
        Map<String, String> header = new HashMap<>(sellerHeaderMap);
        header.put("cookie", cookie);
        return doRequest(url, "post", header, data);
    }

    /**
     * POST请求URL地址
     * @param url 请求地址
     * @param header 请求头
     * @param data 数据列表
     * @return
     */
    private static String doPost(String url, Map<String, String> header, Map<String, Object> data) {
        return doRequest(url, "post", header, data);
    }

    /**
     * POST请求URL地址，使用默认的Header信息，并设置请求Cookie
     * @param url 请求地址
     * @param cookie cookie信息
     * @param data 数据列表
     * @param clazz 返回数据类型
     * @return T类型数据对象
     * @param <T> 返回类型
     */
    private static <T> T doPost(String url, String cookie, Map<String, Object> data, Class<T> clazz) {
        return JSONUtil.toBean(doPost(url, cookie, data), clazz);
    }

    /**
     * POST请求URL地址
     * @param url 请求地址
     * @param header 请求头信息
     * @param data 数据列表
     * @param clazz 返回数据类型
     * @return T类型数据对象
     * @param <T> 返回类型
     */
    private static <T> T doPost(String url, Map<String, String> header, Map<String, Object> data, Class<T> clazz) {
        return JSONUtil.toBean(doPost(url, header, data), clazz);
    }

    /**
     * 网络请求
     * @param url 请求地址
     * @param method 请求方法
     * @param header 请求头
     * @param data 请求参数
     * @return
     */
    private static String doRequest(String url, String method, Map<String, String> header, Map<String, Object> data) {
        HttpRequest request = null;
        switch (method) {
            case "post":
                request = HttpUtil.createPost(url);
                request.body(JSONUtil.toJsonStr(data));
                break;
            default:
                request = HttpUtil.createGet(url);
        }
        request.headerMap(header, true);

        for (int i = 0; i < 3; i++) {
            try {
                return request.execute().body();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        throw new RuntimeException("Web请求异常");
    }

    /**
     * 网络请求，并将请求结果转换为T类型
     * @param url 请求地址
     * @param method 请求方法
     * @param header 请求头
     * @param data 数据列表
     * @param clazz 返回数据类型
     * @return T类型数据对象
     * @param <T> 返回类型
     */
    private static <T> T doRequest(String url, String method, Map<String, String> header, Map<String, Object> data, Class<T> clazz) {
        return JSONUtil.toBean(doRequest(url, method, header, data), clazz);
    }

    /**
     * 遍历全部分页，并将返回结果组转为列表
     * @param fun 处理分页方法
     * @return 完整数据结果
     * @param <T> 返回结果泛型
     */
    private static <T> List<T> getFullListByPage(Function<Long, Page<T>> fun) {
        Long pageTotal = Long.MAX_VALUE;
        List<T> result = new ArrayList<>();
        for (Long i = 1L; i < pageTotal; i++) {
            Page<T> page = fun.apply(i);
            pageTotal = getPageTotal(page.getTotal(), page.getSize());
            result.addAll(page.getRecords());
        }

        return result;
    }
}
